﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace NetFrameworkExtensions.Configuration
{
  public class VariableExpander
  {
    public static readonly Regex VarNameRegex = new Regex(@"\$\((?<varName>\w+)\)", RegexOptions.Compiled | RegexOptions.Singleline);

    public static readonly VariableExpander Default = new VariableExpander();

    private readonly Dictionary<string, string> variables = new Dictionary<string, string>();

    public void Set(string name, string value)
    {
      if (name == null) throw new ArgumentNullException("name");

      if (value != null)
        variables[name] = value;
      else
        variables.Remove(name);
    }

    public void Remove(string name)
    {
      if (name == null) throw new ArgumentNullException("name");
      variables.Remove(name);
    }

    public IEnumerable<string> VariableNames { get { return variables.Keys; } }

    public string this[string name]
    {
      get
      {
        if (name == null) throw new ArgumentNullException("name");
        string value;
        if (variables.TryGetValue(name, out value))
          return value;
        return null;
      }
      set { Set(name, value); }
    }

    public string ExpandVariables(string content)
    {
      if (content == null) throw new ArgumentNullException("content");
      var sb = new StringBuilder();
      int lastPos = -1;
      foreach (var match in VarNameRegex.Matches(content).Cast<Match>())
      {
        if (match.Success)
          foreach (var capture in match.Groups["varName"].Captures.Cast<Capture>()) // only one capture
          {
            // copy from lastPos + 1 to match.Index - 1
            sb.Append(content, lastPos + 1, match.Index - lastPos - 1);

            // append variable value if found
            var value = this[capture.Value];
            if (value == null)
              sb.Append(content, capture.Index, capture.Length);
            else
              sb.Append(value);

            lastPos = match.Index + match.Length - 1;
          }
      }
      sb.Append(content.Substring(lastPos + 1));
      return sb.ToString();
    }
  }
}
